package com.kly.user.rulePolicy;

import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.exception.common.ApplicationException;
import com.google.api.client.util.Lists;
import com.google.common.collect.Maps;
import com.kly.user.rulePolicy.enums.*;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;


/**
 * 策略与规则执行处理类
 * @author linqu
 */
@Service
public class ExecutePolicyService {

    private static final Map<String, ComputeFunction<Variable>> COMPUTE_MAP = Maps.newConcurrentMap();

    static {
        ComputeFunction<Variable> eq = Utils::eq;
        ComputeFunction<Variable> gt = Utils::gt;
        ComputeFunction<Variable> ge = Utils::ge;
        ComputeFunction<Variable> lt = Utils::lt;
        ComputeFunction<Variable> le = Utils::le;
        COMPUTE_MAP.put(OperationEnum.EQ.getValue(), eq);
        COMPUTE_MAP.put(OperationEnum.GT.getValue(), gt);
        COMPUTE_MAP.put(OperationEnum.GE.getValue(), ge);
        COMPUTE_MAP.put(OperationEnum.LT.getValue(), lt);
        COMPUTE_MAP.put(OperationEnum.LE.getValue(), le);
    }



    public Policy processPolicy(Policy policy) {

        RuleComposeEnum ruleCompose = RuleComposeEnum.transformByName(policy.getRuleCompose());
        // 规则集合
        List<Rule> rules = policy.getRuleList();
        if (CollectionUtils.isEmpty(rules)) {
            throw new ApplicationException("该策略未配置有效的规则");
        }

        // 规则执行，执行结果位于Rule.decisionResult中
        processRule(ruleCompose, rules);

        // 根据规则执行结果，设置Policy结果
        final DecisionResultEnum decisionResult = processPolicy(ruleCompose, rules);
        policy.setPolicyResult(decisionResult.getType());

        return policy;
    }

    private DecisionResultEnum processPolicy(RuleComposeEnum ruleCompose, List<Rule> rules) {
        // 规则执行结果
        final List<Decision> ruleDecisionResults = rules.stream().map(Rule::getDecisionResult).collect(Collectors.toList());
        // 首次匹配规则, 一旦命中，决策即中止，后续规则不再起作用
        boolean policyDecisionPass = false;
        if (RuleComposeEnum.FIRST.equals(ruleCompose)) {
            policyDecisionPass = ruleDecisionResults.stream().anyMatch(o -> o.getDecisionResult().equals(DecisionResultEnum.PASS.getType()));
        }

        // 最坏匹配规则, 必须跑完所有的规则，取最坏的规则作为最终的决策结果
        if (RuleComposeEnum.WORST.equals(ruleCompose)) {
            policyDecisionPass = ruleDecisionResults.stream().allMatch(o -> o.getDecisionResult().equals(DecisionResultEnum.PASS.getType()));
        }

        if (policyDecisionPass) {
            return DecisionResultEnum.PASS;
        } else {
            return DecisionResultEnum.REJECT;
        }

    }

    private void processRule(RuleComposeEnum ruleCompose, List<Rule> rules) {
        for (Rule rule : rules) {
            final List<Condition> conditions = rule.getConditions();
            // 参数校验
            if (Objects.isNull(conditions)) {
                throw new ApplicationException("参与计算的比操作符号不存在");
            }

            final ConditionGroupComposeEnum conditionGroupCompose = ConditionGroupComposeEnum.transformByName(rule.getConditionGroupCompose());
            processConditions(conditionGroupCompose, conditions, rule);
        }
    }

    private void processConditions(ConditionGroupComposeEnum conditionGroupCompose, List<Condition> conditions, Rule rule) {

        List<Boolean> conditionResults = Lists.newArrayList();
        for (Condition condition : conditions) {
            // 参数校验
            if (CollectionUtils.isEmpty(conditions)) {
                throw new ApplicationException("参与计算的比操作符号不存在");
            }

            // 条件遍历，并计算一条或多条条件组合的计算结果

            final Variable firstVariable = condition.getFirstVariable();
            final Variable secondVariable = condition.getSecondVariable();
            final boolean computeResult = compute(OperationEnum.transformByName(condition.getOperation()), firstVariable, secondVariable);
            conditionResults.add(computeResult);
        }

        // 全部匹配
        if (conditionGroupCompose.equals(ConditionGroupComposeEnum.ALL)) {
            final boolean allMatch = conditionResults.stream().allMatch(o -> o.equals(Boolean.TRUE));
            if (allMatch) {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.PASS));
            } else {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.REJECT));
            }
        } else if (conditionGroupCompose.equals(ConditionGroupComposeEnum.ANY)) {
            final boolean anyMatch = conditionResults.stream().anyMatch(o -> o.equals(Boolean.TRUE));
            if (anyMatch) {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.PASS));
            } else {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.REJECT));
            }
        } else if (conditionGroupCompose.equals(ConditionGroupComposeEnum.ALL_NOT)) {
            final boolean allNotMatch = conditionResults.stream().allMatch(o -> o.equals(Boolean.FALSE));
            if (allNotMatch) {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.PASS));
            } else {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.REJECT));
            }
        } else if (conditionGroupCompose.equals(ConditionGroupComposeEnum.ANY_NOT)) {
            final boolean anyNotMatch = conditionResults.stream().anyMatch(o -> o.equals(Boolean.FALSE));
            if (anyNotMatch) {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.PASS));
            } else {
                rule.setDecisionResult(buildDecision(DecisionResultEnum.REJECT));
            }
        }

    }

    private Decision buildDecision(DecisionResultEnum decisionResult) {
        Decision decision = new Decision();
        decision.setDecisionName(decisionResult.getValue());
        decision.setDecisionResult(decisionResult.getType());
        return decision;
    }

    private static boolean compute(OperationEnum operationEnum, Variable a1, Variable a2) {
        // 参数校验
        Utils.validVariable(a1);
        Utils.validVariable(a2);
        // 变量计算
        final ComputeFunction<Variable> fun = COMPUTE_MAP.get(operationEnum.getValue());
        return fun.compute(a1, a2);
    }
}
